#include "InterData.h"
#include "Logger.h"
//#include "k3netutils.h"

IDataCore::IDataCore()
{
	// nothing special
	_refCt = 0;
}

IDataCore::~IDataCore()
{
	//LOG(logDEBUG) << "Deleting data; had " << _valList.size() << " elements.";
	for(unsigned int i = 0; i < _valList.size(); ++i)
		delPos(i);
}

IDataCore* IDataCore::duplicate()
{
	IDataCore* ret = new IDataCore;
	for(unsigned int i = 0; i < _valList.size(); ++i)
	{
		ret->_typeList.push_back(_typeList[i]);
		switch(_typeList[i])
		{
		case IV_NONE:
			ret->_valList.push_back(NULL);
			break;
		case IV_INT:
			ret->_valList.push_back(new int(*((int*)(_valList[i]))));
			break;
		case IV_FLOAT:
			ret->_valList.push_back(new float(*((float*)(_valList[i]))));
			break;
		case IV_STRING:
			ret->_valList.push_back(new std::string(*((std::string*)(_valList[i]))));
			break;
		case IV_UCHAR:
			ret->_valList.push_back(new unsigned char(*((unsigned char*)(_valList[i]))));
			break;
		}
	}
	ret->_refCt = 0;
	ret->_nameMap = _nameMap;
	return ret;
}

void IDataCore::delPos(int p)
{
	switch(_typeList[p])
	{
	case IV_NONE:
		break;
	case IV_INT:
		delete ((int*)(_valList[p]));
		break;
	case IV_FLOAT:
		delete ((float*)(_valList[p]));
		break;
	case IV_STRING:
		delete ((std::string*)(_valList[p]));
		break;
	case IV_UCHAR:
		delete ((unsigned char*)(_valList[p]));
		break;
	}
	_typeList[p] = IV_NONE;
	_valList[p] = NULL;
}

int IDataCore::pushNone()
{
	_typeList.push_back(IV_NONE);
	_valList.push_back(NULL);
	return _valList.size() - 1;
}

IData::IData()
{
	_data = new IDataCore;
	++_data->_refCt;
}

IData::IData(int* dummy)
{
	if(dummy != NULL)
		LOG(logWARNING) << "Made interdata using IData(int*) constructor, with non-null argument [the argument does nothing](what are you trying to do?)";
	_data = NULL;
}

IData::IData(const IData& rhs)
{
	_data = rhs._data;
	if(_data)
		++_data->_refCt;
	else
	{
		_data = new IDataCore;
		++_data->_refCt;
	}
}

IData::~IData()
{
	if(_data)
	{
		--_data->_refCt;
		if(_data->_refCt <= 0)
			delete _data;
		_data = NULL;
	}
}

IData& IData::operator=(const IData& rhs)
{
	if(_data)
	{
		--_data->_refCt;
		if(_data->_refCt <= 0)
			delete _data;
		_data = NULL;
	}
	_data = rhs._data;
	if(_data)
		++_data->_refCt;
	else
	{
		_data = new IDataCore;
		++_data->_refCt;
	}
	return *this;
}

IData IData::deepcopy()
{
	IData ret;
	if(_data)
	{
		delete ret._data;
		ret._data = _data->duplicate();
		++ret._data->_refCt;
	}
	return ret;
}

int				IData::geti(int pos)
{
	return interpretIDAs<int>(_data, pos, IV_INT, 0);
}

float			IData::getf(int pos)
{
	return interpretIDAs<float>(_data, pos, IV_FLOAT, 0.0f);
}

std::string		IData::gets(int pos)
{
	return interpretIDAs<std::string>(_data, pos, IV_STRING, "ERROR");
}

unsigned char	IData::getu(int pos)
{
	return interpretIDAs<unsigned char>(_data, pos, IV_UCHAR, 0);
}

std::string		IData::getname(int pos)
{
	std::map<std::string, int>::iterator itr;
	if(_data == NULL)
	{
		LOG(logERROR) << "IData has no actual data (null _data)";
		return "ERROR: NODATA";
	}
	for(itr = _data->_nameMap.begin(); itr != _data->_nameMap.end();)
	{
		if((*itr).second == pos)
			return (*itr).first;
		++itr;
		//(*itr)->update(dt);
	}
	return "";
}

void			IData::seti(int pos, int val)
{
	setIDAs<int>(_data, pos, IV_INT, val);
}

void			IData::setf(int pos, float val)
{
	setIDAs<float>(_data, pos, IV_FLOAT, val);
}

void			IData::sets(int pos, const std::string& val)
{
	setIDAs<std::string>(_data, pos, IV_STRING, val);
}

void			IData::setu(int pos, unsigned char val)
{
	setIDAs<unsigned char>(_data, pos, IV_UCHAR, val);
}

void			IData::setname(int pos, const std::string& name)
{
	// Note: this actually lets you set multiple names for the same position
	// which is a bug turned feature
	_data->_nameMap[name] = pos;
}

void			IData::pushi(int val)
{
	pushIDAs<int>(_data, IV_INT, val);
}

void			IData::pushf(float val)
{
	pushIDAs<float>(_data, IV_FLOAT, val);
}

void			IData::pushs(const std::string& val)
{
	pushIDAs<std::string>(_data, IV_STRING, val);
}

void			IData::pushu(unsigned char val)
{
	pushIDAs<unsigned char>(_data, IV_UCHAR, val);
}

int				IData::getType(int pos)
{
	if(pos < 0 || (unsigned int)pos >= _data->_typeList.size())
		return IV_OUTOFBOUNDS;
	else
		return _data->_typeList[pos];
}

int				IData::size()
{
	if(!_data)
		return 0;
	else
		return _data->_typeList.size();
}

// clears the value, type, and name (sets type to None)
void			IData::clear(int pos)
{
	if(pos < 0 || (unsigned int)pos >= _data->_typeList.size())
		return;
	_data->delPos(pos);
}

// note, if named is called with a nonexistent name, it will return the
// first empty slot as well as give that slot the name
// (so bla.seti(bla.named("foo"), 0, 3) will work even if "foo" does not exist)
int IData::named(const std::string& name)
{
	if(_data->_nameMap.count(name) > 0)
		return _data->_nameMap[name];
	else
	{
		// find first empty slot (actually, be lazy and just push a new thing on the end)
		int np = _data->pushNone();
		_data->_nameMap[name] = np;
		return np;
	}
}

// this is basically just meant for serializers
IDataCore* IData::getRawData()
{
	return _data;
}

// static function for releasing an InterData
// (right now just deletes it)
void InterData::release(InterData* rhs)
{
	if(rhs->isDeletable())
		delete rhs;
}

InterData* InterData::allocate()
{
	return new InterData();
}

InterData* InterData::allocate(int numvals)
{
	return new InterData(numvals);
}


InterData* InterData::allocate(InterData* rhs)
{
	InterData* res = InterData::allocate(rhs->numValues());

	for(int i = 0; i < rhs->numValues(); ++i)
	{
		switch(rhs->canonicalType(i))
		{
		case ID_INT:
			res->pushInt(rhs->getInt(i));
			break;
		case ID_BOOL:
			res->pushBool(rhs->getBool(i));
			break;
		case ID_FLOAT:
			res->pushFloat(rhs->getFloat(i));
			break;
		case ID_STRING:
			res->pushString(rhs->getString(i));
			break;
		case ID_INTARR:
		case ID_BOOLARR:
		case ID_FLOATARR:
		case ID_STRINGARR:
			// TODO: actually push arrays onto the stack;
			res->pushInt(0);
			break;
		default:
			// error!! (but push nil anyway)
			res->pushInt(0);
			break;
		}
	}

	return res;
}

// constructors
InterData::InterData()
{
	// do nothing
	_deletable = true;
}

InterData::InterData(int numvals)
{
	// do nothing
	_deletable = true;
}

void InterData::setDeletable(bool val)
{
	_deletable = val;
}

bool InterData::isDeletable()
{
	return _deletable;
}

// the exact type of the data (there are only
// going to be about two of these)
int InterData::getExactType()
{
	return 0;
}

// number of values
int InterData::numValues()
{
	return (int)_typeList.size();
}

// functions for querying the data
int InterData::isType(int slot, int t)
{
	if(slot < numValues())
		return _typeList[slot] == t;
	else
		return 0;
}

int InterData::canonicalType(int slot)
{
	if(slot < numValues())
		return _typeList[slot];
	else
		return 0;
}

// convenience function for checking if the data could
// be interpreted in a specific way (simpler than making
// a million isBlank calls)
// sig can have the following characters:
// i: int        I: int array
// b: bool       B: bool array
// f: float      F: float array
// s: string     S: string array
//
// e.g., hasSignature("iifB") checks if it has
// two ints followed by a float and then a byte
// array
int InterData::hasSignature(const char* sig)
{
	int pos = 0;
	while(sig[pos] != 0)
	{
		if(pos >= numValues())
			return 0;

		switch(sig[pos])
		{
		case 'i':
			if(!isType(pos, ID_INT))
				return 0;
			break;
		case 'b':
			if(!isType(pos, ID_BOOL))
				return 0;
			break;
		case 'f':
			if(!isType(pos, ID_FLOAT))
				return 0;
			break;
		case 's':
			if(!isType(pos, ID_STRING))
				return 0;
			break;
		case 'I':
			if(!isType(pos, ID_INTARR))
				return 0;
			break;
		case 'B':
			if(!isType(pos, ID_BOOLARR))
				return 0;
			break;
		case 'F':
			if(!isType(pos, ID_FLOATARR))
				return 0;
			break;
		case 'S':
			if(!isType(pos, ID_STRINGARR))
				return 0;
			break;
		default:
			return 0;
		}
	}
	return 1;
}

// functions for getting data
// (note: no error checking! danger!)
int InterData::getInt(int slot)
{
	return *((int*)_valList[slot]);
}

unsigned char InterData::getBool(int slot)
{
	return *((unsigned char*)_valList[slot]);
}

double InterData::getFloat(int slot)
{
	return *((double*)_valList[slot]);
}

std::string InterData::getString(int slot)
{
	return *((std::string*)_valList[slot]);
}

std::vector<int>& InterData::getIntArray(int slot)
{
	return *((std::vector<int>*)_valList[slot]);
}

std::vector<unsigned char>& InterData::getBoolArray(int slot)
{
	return *((std::vector<unsigned char>*)_valList[slot]);
}

std::vector<double>& InterData::getFloatArray(int slot)
{
	return *((std::vector<double>*)_valList[slot]);
}

std::vector<std::string>& InterData::getStringArray(int slot)
{
	return *((std::vector<std::string>*)_valList[slot]);
}

// functions for pushing data (specific to InterData)
void InterData::pushInt(int val)
{
	_typeList.push_back(ID_INT);
	_valList.push_back((void*)(new int(val)));
}

void InterData::pushBool(unsigned char val)
{
	_typeList.push_back(ID_BOOL);
	_valList.push_back((void*)(new unsigned char(val)));
}

void InterData::pushFloat(double val)
{
	_typeList.push_back(ID_FLOAT);
	_valList.push_back((void*)(new double(val)));
}

void InterData::pushString(const std::string& val)
{
	_typeList.push_back(ID_STRING);
	_valList.push_back((void*)(new std::string(val)));
}

void InterData::pushIntArray(std::vector<int>* aval)
{
	_typeList.push_back(ID_INTARR);
	_valList.push_back((void*)aval);
}

void InterData::pushBoolArray(std::vector<unsigned char>* aval)
{
	_typeList.push_back(ID_BOOLARR);
	_valList.push_back((void*)aval);
}

void InterData::pushFloatArray(std::vector<double>* aval)
{
	_typeList.push_back(ID_FLOATARR);
	_valList.push_back((void*)aval);
}

void InterData::pushStringArray(std::vector<std::string>* aval)
{
	_typeList.push_back(ID_STRINGARR);
	_valList.push_back((void*)aval);
}

// functions for setting data
// These are necessary instead of just returning references with the
// getters because other interdata types may not be able to return references
// to their internals...
void InterData::setInt(int slot, int val)
{
	*((int*)_valList[slot]) = val;
}

void InterData::setBool(int slot, unsigned char val)
{
	*((unsigned char*)_valList[slot]) = val;
}

void InterData::setFloat(int slot, double val)
{
	*((double*)_valList[slot]) = val;
}

void InterData::setString(int slot, std::string val)
{
	*((std::string*)_valList[slot]) = val;
}

// functions for serialization
// return number of bytes read or written
int InterData::writeToByteArray(unsigned char* dest)
{
	int pos = 0;
	
	// requires sdl_net to work...

	//// first, write how many fields there are...
	//pos = k3writeInt(dest, pos, _typeList.size());

	//// write fields...
	//int nfields = _typeList.size();
	//for(int i = 0; i < nfields; ++i)
	//{
	//	// write field type
	//	pos = k3writeByte(dest, pos, (unsigned char)_typeList[i]);
	//	switch(_typeList[i])
	//	{
	//	case ID_INT:
	//		LOG(logDEBUG3) << "Wrote INT: " << *((int*)_valList[i]);
	//		pos = k3writeInt(dest, pos, *((int*)_valList[i]));
	//		break;
	//	case ID_BOOL:
	//		LOG(logDEBUG3) << "Wrote BOOL: " << *((unsigned char*)_valList[i]);
	//		pos = k3writeByte(dest, pos, *((unsigned char*)_valList[i]));
	//		break;
	//	case ID_FLOAT:
	//		LOG(logDEBUG3) << "Wrote FLOAT: " << *((double*)_valList[i]);
	//		pos = k3writeFloat(dest, pos, *((double*)_valList[i]));
	//		break;
	//	case ID_STRING:
	//		LOG(logDEBUG3) << "Wrote STRING: " << *((std::string*)_valList[i]);
	//		pos = k3writeString(dest, pos, *((std::string*)_valList[i]));
	//		break;
	//	default:
	//		LOG(logERROR) << "Unknown or unimplemented serialization type " << _valList[i];
	//		break;
	//	}
	//}
	return pos;
}

int InterData::readFromByteArray(unsigned char* src)
{
	int pos = 0;

	// requires sdl_net to work...
	//// first, read how many fields there are...
	//int nfields = 0;
	//int tempInt;
	//double tempFloat;
	//std::string tempString;
	//unsigned char tempByte;
	//pos = k3readInt(src, pos, nfields);
	//LOG(logDEBUG3) << "nfields: " << nfields;

	//// read fields...
	//unsigned char tempType;
	//for(int i = 0; i < nfields; ++i)
	//{
	//	pos = k3readByte(src, pos, tempType);
	//	switch(tempType)
	//	{
	//	case ID_INT:
	//		pos = k3readInt(src, pos, tempInt);
	//		LOG(logDEBUG3) << "Read INT: " << tempInt;
	//		pushInt(tempInt);
	//		break;
	//	case ID_BOOL:
	//		pos = k3readByte(src, pos, tempByte);
	//		LOG(logDEBUG3) << "Read BOOL: " << (int)tempByte;
	//		pushBool(tempByte);
	//		break;
	//	case ID_FLOAT:
	//		pos = k3readFloat(src, pos, tempFloat);
	//		LOG(logDEBUG3) << "Read FLOAT: " << tempFloat;
	//		pushFloat(tempFloat);
	//		break;
	//	case ID_STRING:
	//		pos = k3readString(src, pos, tempString);
	//		LOG(logDEBUG3) << "Read STRING: " << tempString;
	//		pushString(tempString);
	//		break;
	//	default:
	//		LOG(logERROR) << "Unknown or unimplemented serialization type " << _valList[i];
	//		break;
	//	}
	//}
	return pos;
}

void InterData::clear()
{
	for(unsigned int i = 0; i < _typeList.size(); ++i)
	{
		switch(_typeList[i])
		{
		case ID_INT:
			delete ((int*)_valList[i]);
			break;
		case ID_BOOL:
			delete ((unsigned char*)_valList[i]);
			break;
		case ID_FLOAT:
			delete ((double*)_valList[i]);
			break;
		case ID_STRING:
			delete ((std::string*)_valList[i]);
			break;
		case ID_INTARR:
			delete ((std::vector<int>*)_valList[i]);
			break;
		case ID_BOOLARR:
			delete ((std::vector<unsigned char>*)_valList[i]);
			break;
		case ID_FLOATARR:
			delete ((std::vector<double>*)_valList[i]);
			break;
		case ID_STRINGARR:
			delete ((std::vector<std::string>*)_valList[i]);
			break;
		default:
			// error should go here, since we shouldn't ever see a value
			// that isn't one of the above
			break;
		}
	}
	_typeList.clear();
	_valList.clear();
}

void InterData::concat(InterData* rhs)
{
	LOG(logDEBUG3) << "self typelist size: " << _typeList.size();
	LOG(logDEBUG3) << "rhs Typelist size: " << rhs->_typeList.size();
	for(unsigned int i = 0; i < rhs->_typeList.size(); ++i)
	{
		_typeList.push_back(rhs->_typeList[i]);
		_valList.push_back(rhs->_valList[i]);
	}

	rhs->_typeList.clear();
	rhs->_valList.clear();
}

// destructor
InterData::~InterData()
{
	for(unsigned int i = 0; i < _typeList.size(); ++i)
	{
		switch(_typeList[i])
		{
		case ID_INT:
			delete ((int*)_valList[i]);
			break;
		case ID_BOOL:
			delete ((unsigned char*)_valList[i]);
			break;
		case ID_FLOAT:
			delete ((double*)_valList[i]);
			break;
		case ID_STRING:
			delete ((std::string*)_valList[i]);
			break;
		case ID_INTARR:
			delete ((std::vector<int>*)_valList[i]);
			break;
		case ID_BOOLARR:
			delete ((std::vector<unsigned char>*)_valList[i]);
			break;
		case ID_FLOATARR:
			delete ((std::vector<double>*)_valList[i]);
			break;
		case ID_STRINGARR:
			delete ((std::vector<std::string>*)_valList[i]);
			break;
		default:
			// error should go here, since we shouldn't ever see a value
			// that isn't one of the above
			break;
		}
	}
}